package cn.middle.remote.server;

import java.io.File;
import java.net.SocketAddress;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.middle.remote.Server;
import cn.middle.remote.enums.NodeState;
import cn.middle.remote.enums.NodeType;
import cn.middle.remote.handler.decodec.CodecAdapter;
import cn.middle.remote.serialization.JsonSerialization;
import cn.middle.remote.serialization.Serialization;
import cn.middle.remote.util.OsUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.util.concurrent.DefaultThreadFactory;

public class NettyServer extends Server {

	private final Logger log = LoggerFactory.getLogger(NettyServer.class);

	public NettyServer(ServerConfig config) {
		super(config);
	}

	private ServerBootstrap bootstrap;

	private io.netty.channel.Channel channel;

	private EventLoopGroup bossGroup;
	private EventLoopGroup workerGroup;
	private Serialization serialization;

	private NodeState targetState = NodeState.NOT_INITIALIZED;

	@Override
	public NodeType getNodeType() {
		return NodeType.MIDDLE_SERVER;
	}

	@Override
	public boolean isNormal() {
		return targetState == NodeState.CONNECTED;
	}

	@Override
	public boolean isClose() {
		return targetState == NodeState.CLOSE;
	}

	@Override
	public NodeState getNodeState() {
		return targetState;
	}

	@Override
	public void close() {
		targetState = NodeState.CLOSING;
		stop();
		targetState = NodeState.CLOSE;

	}

	@Override
	public void close(int timeout) {
		close();
	}

	@Override
	public SocketAddress getRemoteSocketAddress() {
		return null;
	}

	@Override
	public SocketAddress getLocalSocketAddress() {
		return channel != null ? channel.localAddress() : null;
	}

	@Override
	public void excepton(Throwable t) {
		log.error(t.getMessage(), t);
	}

	@Override
	public void start() {
		front();// 前置工作，初始化业务处理器和序列化器等工作
		targetState = NodeState.CLOSING;
		bootstrap = new ServerBootstrap();
		if (OsUtil.isLinux()) {
			bossGroup = new EpollEventLoopGroup(2, new DefaultThreadFactory("NettyServerBoss", true));
			workerGroup = new EpollEventLoopGroup(this.config.getThreadNumber(),
					new DefaultThreadFactory("NettyServerWorker", true));
		} else {
			bossGroup = new NioEventLoopGroup(2, new DefaultThreadFactory("NettyServerBoss", true));
			workerGroup = new NioEventLoopGroup(this.config.getThreadNumber(),
					new DefaultThreadFactory("NettyServerWorker", true));
		}

		bootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
				.childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
				.childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
				.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
				.childHandler(new ChannelInitializer<NioSocketChannel>() {
					@Override
					protected void initChannel(NioSocketChannel ch) throws Exception {

						CodecAdapter adapter = new CodecAdapter();
						sslContext(ch);
						ch.pipeline().addLast("decoder", adapter.getDecode()).addLast("encoder", adapter.getEncode())
								.addLast("handler", new ServerHandler(handler));
					}
				});

		try {
			// boolean isOpen = false;
			ChannelFuture f = bootstrap.bind(config.getPort()).addListener(future -> {
				if (future.isSuccess()) {
					targetState = NodeState.CONNECTED;
					log.info("open the server port {} is success", config.getPort());
				} else {
					log.error("open the server is false");
				}
			}).sync();
			if (f.isSuccess()) {
				this.channel = f.channel();
			}
			f.channel().closeFuture().addListener(future -> {
				close();
			});

		} catch (InterruptedException e) {
			log.error("open the port {} server is error", this.config.getPort());
		}

	}

	// 参考文档 https://www.cnblogs.com/codehello/articles/16924966.html
	private void sslContext(Channel ch) {
		if (config.isActiveSSL() && config.getSslConfig() != null) {
			try {
				// 引入SSL安全验证
				File certChainFile = new File(config.getSslConfig().getCertChainPath());
				File keyFile = new File(config.getSslConfig().getKeyPath());
				File rootFile = new File(config.getSslConfig().getCaPath());
				SslContext sslContext = SslContextBuilder.forClient().keyManager(certChainFile, keyFile)
						.trustManager(rootFile).build();
				ch.pipeline().addLast(sslContext.newHandler(ch.alloc()));
			} catch (Throwable t) {
				log.error(t.getMessage(), t);
			}
		}
	}

	@Override
	public void stop() {
		if (this.handler != null) {
			this.handler.clearUp();
		}
		if (bossGroup != null) {
			bossGroup.shutdownGracefully();
		}

		if (workerGroup != null) {
			workerGroup.shutdownGracefully();
		}

	}

	@Override
	public void serialization(Serialization serialization) {
		this.serialization = serialization;
	}

	/**
	 * 启动前的前置工作
	 */
	private void front() {
		if (config == null) { // 配置文件为空，直接抛空异常
			throw new NullPointerException("server config is null");
		}

		if (this.authHandler == null) { //鉴权处理器为空,直接抛空异常
			throw new NullPointerException("server authHandler is null");
		}
 
		if (this.listener == null) {  //监听器为空 直接抛空异常
			throw new NullPointerException("server listener is null");
		}

		
		if (serialization == null) { // 序列化处理为空时，默认使用json做为序列化
			serialization = new JsonSerialization();
		}
		this.handler.setLoginAuthHandler(this.authHandler);
		this.handler.setReceivedListener(this.listener);
		this.handler.setSerialization(serialization);
	}

}
